﻿#region Copyright and Licensing Notice
// Copyright (C) 2011 Steven Bosnick
// 
// This program is open source software; you can redistribute it 
// and/or modifiy it under the terms of the Eclipse Public License,
// version 1.0.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the Eclipse Public License for more
// details.
// 
// You should have received a copy of the Eclipse Public License 
// along with this program; if not, you can find a copy at
// "http://www.opensource.org/licenses/eclipse-1.0.php".
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BitwiseOre.SharedMemory.Core.Common;

namespace BitwiseOre.SharedMemory.Core.IPCQueues
{
    internal sealed class GuardedEnumerator<T> : IGuardedEnumerator<T> where T : class 
    {
        private readonly ISemaphore _guard;
        private readonly ISemaphore _signal;
        private readonly IEnumerator<T> _enumerator;

        private bool _enumerationEnded;

        public GuardedEnumerator(ISemaphore guard, ISemaphore signal, IEnumerator<T> enumerator)
        {
            _guard = guard;
            _signal = signal;
            _enumerator = enumerator;
            Current = null;
        }

        public T Current { get; private set; }

        public Task<bool> MoveNext(TimeSpan timeOut)
        {
            if (_enumerationEnded) return GetPastEndTask();

            Flush();

            return _guard.Wait(timeOut)
                .Then(() =>
                          {
                              var result = _enumerator.MoveNext();
                              if (result) Current = _enumerator.Current;
                              return result;
                          });
        }

        public void Flush()
        {
            if (Current == null) return;

            _signal.Release();
            Current = null;
        }

        public void EndEnumeration()
        {
            Flush();
            _enumerationEnded = true;
        }

        private static Task<bool> GetPastEndTask()
        {
            var source = new TaskCompletionSource<bool>(TaskContinuationOptions.AttachedToParent);
            source.SetResult(false);
            return source.Task;
        }
    }
}